var __extends = this.__extends || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};
var Pjs2;
(function (Pjs2) {
    var Runtime = (function () {
        function Runtime() {
            this.functions = {};
            this.aliases = {};
            this.variables = { "test": "ok" };
            this.operators = {
                "+": function (p) {
                    var r;
                    ;
                    p.forEach(function (n, i) {
                        if (i === 0)
                            r = n;
                        else
                            r += n;
                    });
                    return r;
                },
                "-": function (p) {
                    var r = 0;
                    p.forEach(function (n, i) {
                        if (i === 0)
                            r = n;
                        else
                            r -= n;
                    });
                    return r;
                },
                "*": function (p) {
                    var r;
                    p.forEach(function (n, i) {
                        if (i === 0)
                            r = n;
                        else
                            r *= n;
                    });
                    return r;
                },
                "/": function (p) {
                    var r = 1;
                    p.forEach(function (n, i) {
                        if (i == 0)
                            r = n;
                        else
                            r /= n;
                    });
                    return r;
                },
                ",": function (p) {
                    return p;
                }
            };
            this.unaryOperators = {};
        }
        Runtime.prototype.GetFunction = function (name) {
            if (name == null)
                return null;
            name = name.toLowerCase();
            return this.functions[name] || this.GetFunction(this.aliases[name]);
        };

        Runtime.prototype.AddFunction = function (fn) {
            this.functions[fn.name.toLowerCase()] = fn;
        };

        Runtime.prototype.GetVariable = function (name) {
            var v = this.variables[name];
            if (v != null)
                return v;
            else
                return Runtime.constants[name];
        };

        Runtime.prototype.SetVariable = function (name, value) {
            this.variables[name] = value;
        };

        Runtime.prototype.AddAlias = function (name, forFunc) {
            this.aliases[name.toLowerCase()] = forFunc;
        };
        Runtime.constants = {
            "true": true,
            "false": false,
            "null": null
        };
        return Runtime;
    })();
    Pjs2.Runtime = Runtime;

    var NestedRuntime = (function (_super) {
        __extends(NestedRuntime, _super);
        function NestedRuntime(parent) {
            _super.call(this);
            this.parent = parent;
        }
        Object.defineProperty(NestedRuntime.prototype, "operators", {
            get: function () {
                return this.parent.operators;
            },
            enumerable: true,
            configurable: true
        });

        NestedRuntime.prototype.GetVariable = function (name) {
            if (this.variables[name] == null)
                return _super.prototype.GetVariable.call(this, name);
            return this.variables[name];
        };

        NestedRuntime.prototype.GetFunction = function (name) {
            if (this.functions[name.toLowerCase()] == null)
                return _super.prototype.GetFunction.call(this, name);
            return this.functions[name.toLowerCase()];
        };
        return NestedRuntime;
    })(Runtime);
    Pjs2.NestedRuntime = NestedRuntime;

    var FunctionDefinition = (function () {
        function FunctionDefinition(doFunc, parameters, name, sourceScript) {
            if (typeof doFunc === "undefined") { doFunc = null; }
            if (typeof parameters === "undefined") { parameters = null; }
            if (typeof name === "undefined") { name = null; }
            if (typeof sourceScript === "undefined") { sourceScript = null; }
            this.doFunc = doFunc;
            this.parameters = parameters;
            this.name = name;
            this.sourceScript = sourceScript;
        }
        FunctionDefinition.prototype.Call = function (rnt, par, pipeline) {
            var _this = this;
            if (typeof pipeline === "undefined") { pipeline = null; }
            for (var p in par) {
                var name = p;
                if (p.match(/^_pos_\d+$/)) {
                    var index = parseInt(p.substr(5));
                    if (this.parameters.length > index) {
                        name = this.parameters[index].name;
                        if (par[name] != undefined)
                            throw "wtf positional parameter " + name;
                        par[name] = par[p];
                    }
                }
                name = name.toLowerCase();
                if (par[name] == undefined)
                    par[name] = par[p];
            }

            //  convert parameters
            this.parameters.forEach(function (v) {
                if (par[v.name.toLowerCase()] !== undefined) {
                    var convert = _this.TryConvert(par[v.name.toLowerCase()], v);
                    if (convert === undefined)
                        throw "typecheck failed for parameter " + v.name + " on function " + _this.name;
                    if (convert !== par[v.name.toLowerCase()])
                        par[v.name] = par[v.name.toLowerCase()] = convert;
                }
            });

            //map value form pipeline
            var calls = pipeline == null ? [par] : this.MapPipelineValue(pipeline, par);

            var rpar = this.parameters.filter(function (p) {
                return p.required;
            });

            //call
            var res = calls.map(function (c) {
                //chack parameters
                rpar.forEach(function (v) {
                    if (c[v.name.toLowerCase()] == undefined)
                        throw "required parameter (" + v.name + ") is not here";
                });
                return _this.doFunc(rnt, c);
            });

            // TODO: IEnumerable
            if (res.length == 1)
                return res[0];
            else if (res.length == 0)
                return null;
            else
                return res;
        };

        FunctionDefinition.prototype.MapPipelineValue = function (pipeline, par) {
            var _this = this;
            var pipar = this.parameters.filter(function (p) {
                return p.allowPipeline && par[p.name] === undefined;
            });
            var res = [];
            pipar.forEach(function (p) {
                if (p.typecheck(pipeline) && res.length == 0) {
                    par[p.name] = par[p.name.toLowerCase()] = pipeline;
                    res.push(par);
                }
            });
            pipar.forEach(function (p) {
                var convert;
                if ((convert = _this.TryConvert(pipeline, p)) !== undefined && res.length == 0) {
                    par[p.name] = par[p.name.toLowerCase()] = convert;
                    res.push(par);
                }
            });
            if (res.length > 0)
                return res;

            if (pipeline instanceof Array) {
                var calls = new Array();
                var possible = pipeline.every(function (v) {
                    try  {
                        var par2 = shallowCopy(par);
                        var r = _this.MapPipelineValue(v, par2);
                        calls.push(par2);
                        return true;
                    } catch (ex) {
                        return false;
                    }
                });
                if (possible)
                    return calls;
            }

            throw "can't map pipeline value to function " + this.name;
        };

        FunctionDefinition.prototype.TryConvert = function (value, p) {
            if (p.typecheck && !p.typecheck(value))
                return p.typeconvert(value);
            else
                return value;
        };

        FunctionDefinition.FromScript = function (script, name, parameters) {
            return new FunctionDefinition(function (r, params) {
                var nr = new NestedRuntime(r);
                for (var p in params) {
                    if (p.substr(0, 5) == "_pos_") {
                        var index = parseInt(p.substr(5));
                        var name = parameters[index].name;
                        nr.SetVariable(name, params[p].Do ? params[p].Do(r) : params[p]);
                    } else {
                        nr.SetVariable(p, params[p].Do ? params[p].Do(r) : params[p]);
                    }
                }

                return script.Run(nr);
            }, parameters, name, script.source);
        };
        return FunctionDefinition;
    })();
    Pjs2.FunctionDefinition = FunctionDefinition;

    var FuncParameterDefinition = (function () {
        function FuncParameterDefinition(name, required, allowPipeline, //public allowPositional = true,
        typecheck, typeconvert) {
            if (typeof required === "undefined") { required = false; }
            if (typeof allowPipeline === "undefined") { allowPipeline = true; }
            if (typeof typecheck === "undefined") { typecheck = function (o) {
                return true;
            }; }
            if (typeof typeconvert === "undefined") { typeconvert = function (o) {
                return typecheck(o) ? o : undefined;
            }; }
            this.name = name;
            this.required = required;
            this.allowPipeline = allowPipeline;
            this.typecheck = typecheck;
            this.typeconvert = typeconvert;
        }
        return FuncParameterDefinition;
    })();
    Pjs2.FuncParameterDefinition = FuncParameterDefinition;

    var Script = (function () {
        function Script(commands, source) {
            if (typeof commands === "undefined") { commands = new Array(); }
            if (typeof source === "undefined") { source = null; }
            this.commands = commands;
            this.source = source;
        }
        Script.prototype.Run = function (r) {
            var result = new Pjs2.EnumBuilder();
            this.runInternal(r, 0, result);
            return result;
        };

        Script.prototype.runInternal = function (r, index, result) {
            var _this = this;
            if (index == this.commands.length) {
                result.end();
                return;
            }
            var cres = this.commands[index].Do(r);
            this.procResult(cres, result).OnReturn(function () {
                return _this.runInternal(r, index + 1, result);
            });
        };

        Script.prototype.procResult = function (res, builder) {
            var _this = this;
            if (typeof res.getEnumerator == "function") {
                return this.procResEnumerable(res, builder);
            } else if (res instanceof Pjs2.ReturnCallback) {
                return Pjs2.ReturnCallback.ProcessOne(res, function (value) {
                    return _this.procResult(value, builder);
                });
            } else if (res != null)
                builder.addValue(res);
            return Pjs2.ReturnCallback.Done(true);
        };

        Script.prototype.procResEnumerable = function (res, builder) {
            var _this = this;
            var rc = new Pjs2.ReturnCallback();
            return Pjs2.Enumerable.forEach(res, function (value) {
                return _this.procResult(value, builder);
            });
        };
        return Script;
    })();
    Pjs2.Script = Script;

    var ScriptBlockCreate = (function () {
        function ScriptBlockCreate(script) {
            this.script = script;
        }
        ScriptBlockCreate.prototype.Do = function (r) {
            return this.script;
        };
        return ScriptBlockCreate;
    })();
    Pjs2.ScriptBlockCreate = ScriptBlockCreate;

    var StringCreate = (function () {
        function StringCreate(value) {
            this.value = value;
        }
        StringCreate.prototype.Do = function (r) {
            return this.value;
        };
        return StringCreate;
    })();
    Pjs2.StringCreate = StringCreate;

    var NumberCreate = (function () {
        function NumberCreate(value) {
            this.value = value;
        }
        NumberCreate.prototype.Do = function (r) {
            return this.value;
        };
        return NumberCreate;
    })();
    Pjs2.NumberCreate = NumberCreate;

    var Cast = (function () {
        function Cast(value, name) {
            this.value = value;
            this.name = name;
        }
        Cast.prototype.Do = function (r) {
            throw "casting not supported";
        };
        return Cast;
    })();
    Pjs2.Cast = Cast;

    var MethodCall = (function () {
        function MethodCall(on, name, parameters) {
            if (typeof parameters === "undefined") { parameters = new Array(); }
            this.on = on;
            this.name = name;
            this.parameters = parameters;
        }
        MethodCall.prototype.Do = function (r) {
            var on = this.on.Do(r);
            var f = on[this.name];
            if (f == null) {
                throw "function '" + this.name + "' was not found";
            }
            return f.apply(on, this.parameters.map(function (p) {
                return p.Do(r);
            }));
        };
        return MethodCall;
    })();
    Pjs2.MethodCall = MethodCall;

    var Property = (function () {
        function Property(on, name) {
            this.on = on;
            this.name = name;
        }
        Property.prototype.Do = function (r) {
            var _this = this;
            return Pjs2.ReturnCallback.ProcessOne(this.on.Do(r), function (d) {
                return d[_this.name];
            });
        };
        return Property;
    })();
    Pjs2.Property = Property;

    var VariableCall = (function () {
        function VariableCall(name) {
            this.name = name;
        }
        VariableCall.prototype.Do = function (r) {
            return r.GetVariable(this.name);
        };
        return VariableCall;
    })();
    Pjs2.VariableCall = VariableCall;

    var VariableSet = (function () {
        function VariableSet(vname, content, typeAccelerator) {
            if (typeof typeAccelerator === "undefined") { typeAccelerator = null; }
            this.vname = vname;
            this.content = content;
            this.typeAccelerator = typeAccelerator;
        }
        VariableSet.prototype.Do = function (r) {
            var cont = this.content.Do(r);

            // TODO: type accelerator implement
            r.SetVariable(this.vname, cont);
        };
        return VariableSet;
    })();
    Pjs2.VariableSet = VariableSet;

    var PropertySet = (function () {
        function PropertySet(property, value) {
            this.property = property;
            this.value = value;
        }
        PropertySet.prototype.Do = function (r) {
            var _this = this;
            var on = this.property.on.Do(r);
            return Pjs2.ReturnCallback.ProcessOne(on, function (o) {
                return Pjs2.ReturnCallback.ProcessOne(_this.value.Do(r), function (v) {
                    return o[_this.property.name] = v;
                });
            });
        };
        return PropertySet;
    })();
    Pjs2.PropertySet = PropertySet;

    var Condition = (function () {
        function Condition(condition, ifScript, elseScript) {
            if (typeof elseScript === "undefined") { elseScript = new Script(); }
            this.condition = condition;
            this.ifScript = ifScript;
            this.elseScript = elseScript;
        }
        Condition.prototype.Do = function (r) {
            var _this = this;
            var rc = new Pjs2.ReturnCallback();
            Pjs2.ReturnCallback.ProcessOne(this.condition.Do(r), function (d) {
                if (d) {
                    rc.Return(_this.ifScript.Run(r));
                } else {
                    rc.Return(_this.elseScript.Run(r));
                }
            });
            return rc;
        };
        return Condition;
    })();
    Pjs2.Condition = Condition;

    var UnaryOperator = (function () {
        function UnaryOperator(operator, command) {
            this.operator = operator;
            this.command = command;
        }
        UnaryOperator.prototype.Do = function (r) {
            throw "unary operators are not implemented";
        };
        return UnaryOperator;
    })();
    Pjs2.UnaryOperator = UnaryOperator;

    var Operator = (function () {
        function Operator(operator, commands) {
            this.operator = operator;
            this.commands = commands;
        }
        Operator.prototype.Do = function (r) {
            var _this = this;
            return Pjs2.ReturnCallback.Process(this.commands.map(function (c) {
                return c.Do(r);
            }), function (d) {
                return r.operators[_this.operator](d);
            });
        };
        return Operator;
    })();
    Pjs2.Operator = Operator;

    var Call = (function () {
        function Call(name, parameters, pipeline) {
            if (typeof parameters === "undefined") { parameters = {}; }
            if (typeof pipeline === "undefined") { pipeline = null; }
            this.name = name;
            this.parameters = parameters;
            this.pipeline = pipeline;
        }
        Call.prototype.Do = function (r) {
            var f = r.GetFunction(this.name);
            if (f == null)
                throw "function '" + this.name + "' was not found";
            var parray = [];
            var pnames = [];
            for (var p in this.parameters) {
                parray.push(this.parameters[p].Do(r));
                pnames.push(p);
            }
            var pipelineValue = this.pipeline == null ? null : this.pipeline.Do(r);
            return Pjs2.ReturnCallback.Process(parray, function (arr) {
                var params = {};
                for (var i = 0; i < arr.length; i++) {
                    params[pnames[i]] = arr[i];
                }
                return Pjs2.ReturnCallback.ProcessOne(pipelineValue, function (p) {
                    return f.Call(r, params, p);
                });
            });
        };
        return Call;
    })();
    Pjs2.Call = Call;

    var StaticClass = (function () {
        function StaticClass(path) {
            this.path = path;
        }
        StaticClass.prototype.Do = function (r) {
            var c = window;
            for (var i = 0, len = this.path.length; i < len; i++) {
                c = c[this.path[i]];
            }

            return c;
        };
        return StaticClass;
    })();
    Pjs2.StaticClass = StaticClass;

    var DefineFunction = (function () {
        function DefineFunction(definition) {
            this.definition = definition;
        }
        DefineFunction.prototype.Do = function (r) {
            r.AddFunction(this.definition);
            return null;
        };
        return DefineFunction;
    })();
    Pjs2.DefineFunction = DefineFunction;

    var NewHashtable = (function () {
        function NewHashtable(fields) {
            this.fields = fields;
        }
        NewHashtable.prototype.Do = function (r) {
            var _this = this;
            return Pjs2.ReturnCallback.Process(this.fields.map(function (v) {
                return v.value.Do(r);
            }), function (objs) {
                return Pjs2.ReturnCallback.Process(_this.fields.map(function (v) {
                    return v.name.Do(r);
                }), function (names) {
                    var obj = {};
                    objs.forEach(function (f, i) {
                        obj[names[i]] = f;
                    });
                    obj.$type = "hashtable";
                    return obj;
                });
            });
        };
        return NewHashtable;
    })();
    Pjs2.NewHashtable = NewHashtable;
})(Pjs2 || (Pjs2 = {}));
//# sourceMappingURL=pjs2-core.js.map
